 
#include <map>
#include <sstream>
#include <vector>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>


#include "clientAcl.h"
#include "configParser.h"
#include "Regex_Host_Parser.h"
#include "Debug.h"

clientAcl * clientAcl::_instance = NULL;

/*
clientAcl * clientAcl::Instance(const char *confFile)
{
	if(confFile == NULL || strlen(confFile) < 4)
		return _instance;
       
	if (NULL == _instance )
	{
		_instance = new clientAcl();
		if (! _instance->init(confFile) )
		{
			delete _instance;
			_instance = NULL;
		}
	}
	return _instance;
}
*/
/*
如果没有confFile,则采用内置的IP，也即仅允许来自本机的请求！
*/
clientAcl * clientAcl::Instance(const char *confFile)
{
      
	if (NULL == _instance )
	{
		_instance = new clientAcl();
		if(confFile == NULL || strlen(confFile) < 4)
			return _instance;

		_instance->init(confFile); 
	}
	return _instance;
}

bool clientAcl::init(const char * confFile)
{
   m_IPs.clear();
   struct stat sbconf;
   if ( stat( confFile, &sbconf) != 0 )
   {
           DEBUG2("file is not exist!:", confFile);
	   return false;
   }
   configParser config;
   vector < string > args = config.LoadConfInfo(confFile);
   
   HostParser hp;
   for(vector <string>::iterator iter = args.begin(); iter != args.end(); iter++)
	   hp.ParseHostString( (*iter));

   m_IPs = hp.GetIPs();
   if(m_IPs.size() > 0)
       return true;
   return false;
}

bool clientAcl:: Check( struct sockaddr_storage * addr)
{
	if( addr == NULL ){
		if(addr == NULL) 
			DEBUG1("addr is null");
		return true;
	}

        string ip = get_in_addr( addr, ( struct sockaddr *)addr) ;
        DEBUG2("check client ip = ", ip); 

	if(strstr(ip.c_str(),"0.0.0.0")!=0 || strstr(ip.c_str(),"127.0.0.1") != 0 
          || strstr(ip.c_str(),"::1")!=0)
		return true;

	map < string,int > ::iterator iter = m_IPs.find(ip);
	if( iter != m_IPs.end() )
		return true;
	DEBUG2("check false: ip=", ip);
	return false;
}

clientAcl::~clientAcl() 
{


}

void clientAcl::Release()
{
	if (_instance )
	{
		delete _instance;
		_instance = NULL;
	}
}

void clientAcl::Print()
{
        cout<<"+++++++++++++       acl for client ip      ++++++++++++++"<<endl;
	map < string,int > ::iterator iter;
	
	for(iter = m_IPs.begin(); iter!= m_IPs.end(); iter++)
		cout<<"+\t"<< iter->first.c_str() << endl;

	iter = m_IPs.find("0.0.0.0");
	if(iter == m_IPs.end())
		cout<<"+\t0.0.0.0"<<endl;

	iter = m_IPs.find("127.0.0.1");
	if(iter == m_IPs.end())
		cout<<"+\t127.0.0.1"<<endl;

        cout<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
}

void clientAcl::Report_Restart(int rport, int lport )
{
	map < string,int > ::iterator iter;
	for(iter = m_IPs.begin(); iter!= m_IPs.end(); iter++)
	{
		int sock = connect_remote_socket(iter->first.c_str(), rport);
		if (sock <= 0)
			continue;
		char buf[128];
		int len = sprintf(buf,"memclear:%d\n", lport);
		send(sock, buf, len, 0);
		close(sock);
		printf("report to %s : %d that memcached(%d) has been restart\n", iter->first.c_str(), rport, lport);
	}
}

int clientAcl:: connect_remote_socket(const char *ip, int port )
{
	struct hostent *he;
	struct sockaddr_in their_addr;
	he = gethostbyname(ip);
        int fd = 0;
	if((fd = socket(AF_INET,SOCK_STREAM,0))==-1)
	{
		perror("socketi error!");
		return  0;
	}
	if(he == NULL)
	{
		cerr<<"gethostbyname return NULL "<<endl;
		return 0;
	}

	their_addr.sin_family = AF_INET;
	their_addr.sin_port = htons( port );
	their_addr.sin_addr = *((struct in_addr *)he->h_addr);

        int ret;
        long arg;
        fd_set myset;
        struct timeval tv;
        int valopt;
        socklen_t lon;
        if( (arg = fcntl(fd, F_GETFL, NULL)) < 0)
                return -1;
        arg |= O_NONBLOCK;
        if( fcntl(fd, F_SETFL, arg) < 0)
                return -1;
        if(connect(fd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) < 0)
        {
                if (errno == EINPROGRESS)
                {
                        do {
                                tv.tv_sec = 2;
                                tv.tv_usec = 0;
                                FD_ZERO(&myset);
                                FD_SET(fd, &myset);
                                ret = select(fd+1, NULL, &myset, NULL, &tv);
                                if (ret < 0 && errno != EINTR)
                                        return -1;
                                else if (ret > 0)
                                {
                                        lon = sizeof(int);
                                        if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0)
                                                return -1;
                                        if (valopt)
                                                return -1;
                                        break; // success to connect !!! 
                                }
                                else
                                        return -3;
                        } while (1);
                }

                else
                        return -1;
        }
        if( (arg = fcntl(fd, F_GETFL, NULL)) < 0)
        {
                close(fd);
                return -1;
        }
        arg &= (~O_NONBLOCK);
        if( fcntl(fd, F_SETFL, arg) < 0)
        {
                close(fd);
                return -1;
        }
        return fd;
}

string clientAcl :: get_in_addr(struct sockaddr_storage *sst, struct sockaddr *sa)
{
	char s[INET6_ADDRSTRLEN];
	if (sa->sa_family != AF_INET) {
		inet_ntop(sst->ss_family, &(((struct sockaddr_in6*)sa)->sin6_addr),  s, sizeof s);
		DEBUG2("using ipv6 =   ", s);
	}
	else
		inet_ntop(sst->ss_family,  &(((struct sockaddr_in*)sa)->sin_addr) , s, sizeof s);
	string ip = s;
	if(ip.length()< 7)
		ip = "127.0.0.1";
	return ip;
}

